package org.example.executor.impl;

import com.sun.deploy.ui.AboutDialog;
import org.example.config.Configurations;
import org.example.dto.BoundSql;
import org.example.dto.SqlData;
import org.example.executor.Executor;

import javax.print.DocFlavor;
import javax.sql.DataSource;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.*;

public class ExecutorImpl implements Executor {
    @Override
    public <T> List<T> query(Configurations configurations, SqlData sqlData, Object... args) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, IntrospectionException, InvocationTargetException {
        DataSource dataSource = configurations.getDataSource();
        Connection connection = dataSource.getConnection();

        BoundSql boundSql = handleSql(sqlData.getSql());

        //
        String parameterType = sqlData.getParameterType();
        String sql = boundSql.getSql();
        List<String> paramsList = boundSql.getParamsList();
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        for (int i = 0; i < paramsList.size(); i++) {
            //参数替换，通过Method方法获取某个对象中某个对应的值
            String param = paramLists.get(i);
            Object arg = args[0];

            Class<?> type = getType(parameterType);

            //获取字段的对象，通过字段对象去匹配赋值
            Field declaredField = type.getDeclaredField(param);
            declaredField.setAccessible(true);

            Object filedValue = declaredField.get(arg);

            preparedStatement.setObject(i + 1, filedValue);
        }
        ResultSet resultSet;
        if ("select".equals(sqlData.getType())) {
            resultSet = preparedStatement.executeQuery();
        } else {
            int i = preparedStatement.executeUpdate();
            List<Integer> list = new ArrayList<>();
            list.add(i);
            return (List<T>) list;
        }

        if (resultSet == null)
        {
            return null;
        }

        List<Object> resultList = new ArrayList<>(resultSet.getRow());
        String resultType = sqlData.getResultType();
        while (resultSet.next())
        {
            ResultSetMetaData metaData = resultSet.getMetaData();
            //获取列数
            int columnCount = metaData.getColumnCount();
            //创建一个返回类型的类对象
            Class<?> returnType = getType(resultType);
            //通过类对象创建一个返回对象
            Object instance = returnType.newInstance();
            for (int i = 1; i <= columnCount; i++) {
                //获取列的名字
                String columnName = metaData.getColumnName(i);
                System.out.println(columnName);
                //通过列的名字取值，并将列返回
                Object result = resultSet.getObject(columnName);
                System.out.println(result);
                //将列名与返回的类对象进行匹配
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,returnType);
                //得到一个写入方法
                Method writeMethod = propertyDescriptor.getWriteMethod();
                //将值写入到这个对象当中
                writeMethod.invoke(instance,result);
            }
            resultList.add(instance);
        }


        return (List<T>) resultList;
    }

    private Map<Integer,Integer> mapIndex = new LinkedHashMap<>();

    private List<String> paramLists = new ArrayList<>();

    private int fromIndex = 0;
    private BoundSql handleSql(String sql)
    {
        submitSql(sql);
        for (Map.Entry<Integer, Integer> entry : mapIndex.entrySet()) {
            Integer begin = entry.getKey();
            Integer end = entry.getValue();

            String substring = sql.substring(begin + 2, end);
            paramLists.add(substring);
        }
        BoundSql boundSql = new BoundSql();
        for (String paramList : paramLists) {

            sql = sql.replace("#{" + paramList + "}", "?");
            boundSql.getParamsList().add(paramList);
        }

        boundSql.setSql(sql);
        //boundSql.setParamsList(paramLists);
        return boundSql;
    }

    private void submitSql(String sql) {
        int beginIndex = sql.indexOf("#{", fromIndex);
        if (beginIndex != -1)
        {
            int endIndex = sql.indexOf("}", fromIndex + 1);
            if (endIndex != -1)
            {
                fromIndex = endIndex+1;
                mapIndex.put(beginIndex,endIndex);
                submitSql(sql);
            }
        }
    }

    private Class<?> getType(String type) throws ClassNotFoundException {
        if (null == type || type.equals(""))
        {
            return null;
        }
        return Class.forName(type);
    }

}
